/*
 * The MIT License
 *
 * Copyright 2011 Roman Yakovenko <erelus@gmail.com>.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.stego.alg;

import com.stego.core.BitOperations;
import com.stego.core.Transformer;
import com.stego.image.Image;
import com.stego.image.Pixel;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

public class LSBAlg implements Transformer {

    private File file;

    public LSBAlg(File file) {
        this.file = file;
    }

    @Override
    public Pixel[][] hideData(byte[] data) throws IOException {
        int bitOffset = 0;
        Image image = new Image(this.file);
        Pixel[][] pixels = image.getPixels();
        for (int i = 0; i < pixels.length; i++) {
            for (int j = 0; j < pixels[i].length; j++) {
                int block;
                int pixel;
                byte bitToHide;

                block = bitOffset / 8;
                pixel = bitOffset % 8;
                int red = pixels[i][j].getRed();
                if (block < data.length) {
                    bitToHide = BitOperations.getBit(pixel, data[block]);
                } else {
                    bitToHide = 0;
                }
                red = (red & 0xFE) | bitToHide;
                pixels[i][j].setRed(red);
                bitOffset++;

                block = bitOffset / 8;
                pixel = bitOffset % 8;
                int green = pixels[i][j].getGreen();
                if (block < data.length) {
                    bitToHide = BitOperations.getBit(pixel, data[block]);
                } else {
                    bitToHide = 0;
                }
                green = (green & 0xFE) | bitToHide;
                pixels[i][j].setGreen(green);
                bitOffset++;

                block = bitOffset / 8;
                pixel = bitOffset % 8;
                int blue = pixels[i][j].getBlue();
                if (block < data.length) {
                    bitToHide = BitOperations.getBit(pixel, data[block]);
                } else {
                    bitToHide = 0;
                }
                blue = (blue & 0xFE) | bitToHide;
                pixels[i][j].setBlue(blue);
                bitOffset++;
            }
        }
        return pixels;
    }

    @Override
    public byte[] getData() throws IOException {
        Image image = new Image(this.file);
        Pixel[][] container = image.getPixels();
        int size = 3 * container.length * container[0].length / 8 + 1;
        byte[] result = new byte[size];
        Arrays.fill(result, (byte) 0);
        int dataOffset = 0; //смещение в битах в массиве данных.
        for (int i = 0; i < container.length; i++) {
            for (int j = 0; j < container[i].length; j++) {
                int block;
                int pixel;
                int bitToOpen;

                block = dataOffset / 8;
                pixel = dataOffset % 8;
                int red = container[i][j].getRed();
                bitToOpen = BitOperations.getBit(LSB, (byte) red);
                result[block] = (byte) (result[block] | (bitToOpen << pixel));
                dataOffset++;

                block = dataOffset / 8;
                pixel = dataOffset % 8;
                int green = container[i][j].getGreen();
                bitToOpen = BitOperations.getBit(LSB, (byte) green);
                result[block] = (byte) (result[block] | (bitToOpen << pixel));
                dataOffset++;

                block = dataOffset / 8;
                pixel = dataOffset % 8;
                int blue = container[i][j].getBlue();
                bitToOpen = BitOperations.getBit(LSB, (byte) blue);
                result[block] = (byte) (result[block] | (bitToOpen << pixel));
                dataOffset++;
            }
        }
        return result;
    }
}
